#ifndef UMLRELATION_H
#define UMLRELATION_H

#include <QDebug>

#include <QGraphicsItemGroup>
#include <QGraphicsLineItem>
#include <QGraphicsScene>
#include <QGraphicsPolygonItem>
#include <QMutex>
#include <limits>
#include <QSharedPointer>
#include <QFlags>
#include <QFlag>

#include "uml/umlgraphicsitem.h"
#include "uml/relations/relationgraphicsline.h"
#include "gui/editors/relationeditor.h"
#include "uml/relations/labels/relationnamelabel.h"
#include "uml/class/umlcardinality.h"

#define RIGHT_OF 0
#define LEFT_OF 180
#define ABOVE_OF 270
#define BELOW_OF 90

class UmlRelationalItem;
class RelationPort;

class UmlRelation : public UmlGraphicsItem
{
    Q_OBJECT
public:
    enum RelativeItemPosition {
        NORTH,
        SOUTH,
        WEST,
        EAST
    };

    enum RelationType {
        UmlDirectedRelationType         = 1,
        UmlUndirectedRelationType       = 2,
        UmlGeneralizationRelationType   = 4,
        UmlRealizationRelationType      = 8,
        UmlAssociationRelationType      = 16,
        UmlCompositionRelationType      = 32,
        UmlAggregationRelationType      = 64
    };

    Q_DECLARE_FLAGS(RelationTypes, RelationType);

    UmlRelation(RelationTypes relationType = UmlAssociationRelationType, QGraphicsItem *parent = 0);
    ~UmlRelation();

    bool relationIsOfType(RelationTypes relationType);
    UmlRelation::RelationTypes relationType();
    void setRelationType(UmlRelation::RelationTypes relationType);

    virtual void setText(QString name);

    UmlRelationalItem* item1();
    UmlCardinality cardinalityItem1() const;
    void setCardinalityItem1(const UmlCardinality cardinality);
    QString roleItem1();
    void setRoleItem1(QString role);
    RelationPort* port1();
    UmlRelation::RelativeItemPosition item1Direction();

    UmlRelationalItem* item2();
    UmlCardinality cardinalityItem2() const;
    void setCardinalityItem2(const UmlCardinality cardinality);
    QString roleItem2();
    void setRoleItem2(QString role);
    RelationPort* port2();
    UmlRelation::RelativeItemPosition item2Direction();

    UmlRelationalItem* destinationItem();

    bool isDirected();
    void setDirectionTowards(UmlRelationalItem *item);
    void removeDirection();

    void setItems(UmlRelationalItem *item1, UmlRelationalItem *item2);

    bool isValid();

    UmlRelation::RelativeItemPosition relativeRelationDirection(UmlRelationalItem *baseItem);

    QVariant itemChange(GraphicsItemChange change, const QVariant &value);

    void drawRelationLine();

    QPen linePenForRelationType(RelationTypes relationType);

    virtual QList<QAction*> contextActions();

protected:
    RelationTypes _relationType;

    UmlRelationalItem *_item1;
    RelativeItemPosition _item1Direction;
    UmlCardinality _cardinalityItem1;
    QString _roleItem1;
    RelationPort *_port1;

    UmlRelationalItem *_item2;
    RelativeItemPosition _item2Direction;
    UmlCardinality _cardinalityItem2;
    QString _roleItem2;
    RelationPort *_port2;

    RelationGraphicsLine _extensionLine1GraphicsItem;
    RelationGraphicsLine _extensionLine2GraphicsItem;
    RelationGraphicsLine _crossLineGraphicsItem;

    UmlRelationalItem *_destinationItem;

    QMutex _mutex;

    QString directionToString(RelativeItemPosition direction);

    void showName(bool visible);
    RelationLabel _nameLabel;

    QPen _pen;

    void drawDirectedLine();
    void drawUndirectedLine();
    void drawName(QPoint p1, QPoint p2, UmlRelation::RelativeItemPosition &direction);

public slots:
    void itemPositionHasChanged();

    void relationHoverEnter(QGraphicsSceneHoverEvent *event);
    void relationHoverLeave(QGraphicsSceneHoverEvent *event);
    void relationHoverMove(QGraphicsSceneHoverEvent *event);
    void relationMouseDoubleClick(QGraphicsSceneMouseEvent *event);

protected slots:
    virtual void remove();
    void setIsGeneralization();
    void setIsRealization();
    void setIsAssociation();
    void setIsAggregation();
    void setIsComposition();
    void changeDirection();

private slots:
    void changeRelationType(RelationTypes relationType);

signals:
    void directionHasChanged();
    void cardinalityHasChanged();
};

Q_DECLARE_OPERATORS_FOR_FLAGS(UmlRelation::RelationTypes);

typedef QSharedPointer<UmlRelation> P_UmlRelation;

#endif // UMLRELATION_H
